// Copyright 2011 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
 * @fileoverview Generic visitor for the AST generated by glsl.pegjs.
 * @author rowillia@google.com (Roy Williams)
 */
goog.provide('glslunit.ASTVisitor');

goog.require('glslunit.utils');
goog.require('goog.array');



/**
 * Creates an ASTVisitor.
 * @constructor
 */
glslunit.ASTVisitor = function() {
};


/**
 * Gets a function to be called before visiting a node.  This function will
 *     be called before visiting a node's children and the node.
 * @param {!Object} node The node being dispatched to a visit call.
 * @return {function(!Object)} The function to call before visit.
 */
glslunit.ASTVisitor.prototype.getBeforeVisitFunction = function(node) {
  return this['beforeVisit' + glslunit.utils.getFunctionSuffix(node.type)];
};


/**
 * Gets a a function to be called after a node is finished being visited.
 * @param {!Object} node The node being dispatched to a visit call.
 * @return {function(!Object)} The function to call after visit.
 */
glslunit.ASTVisitor.prototype.getAfterVisitFunction = function(node) {
  return this['afterVisit' + glslunit.utils.getFunctionSuffix(node.type)];
};


/**
 * Gets the visit function for a given node.
 * @param {!Object} node The node to get the visit function for.
 * @return {function(!Object)} The visit function for the node passed in.
 */
glslunit.ASTVisitor.prototype.getVisitFunction = function(node) {
  return this['visit' + glslunit.utils.getFunctionSuffix(node.type)];
};


/**
 * Dispatches calls to different Visit methods based off of a node's type.
 * @param {Object} node The node being dispatched to a visit call.
 */
glslunit.ASTVisitor.prototype.visitNode = function(node) {
  if (!node || !node.type) {
    return;
  }
  var beforeVisitFunction = this.getBeforeVisitFunction(node);
  if (beforeVisitFunction) {
    beforeVisitFunction.apply(this, [node]);
  }
  var visitFunction = this.getVisitFunction(node);
  // If the subclass implementing the Visitor implements this function, call it.
  // Otherwise, use the GenericVisitor to recurse through all of this node's
  // children that are AST nodes.
  if (visitFunction) {
   visitFunction.apply(this, [node]);
  } else {
    this.genericVisitor(node);
  }
  var afterVisitFunction = this.getAfterVisitFunction(node);
  if (afterVisitFunction) {
    afterVisitFunction.apply(this, [node]);
  }
};


/**
 * Visits each element in an array.
 * @param {Array.<Object>} nodes The nodes being dispatched to visit calls.
 * @protected
 */
glslunit.ASTVisitor.prototype.visitList = function(nodes) {
  goog.array.forEach(nodes, function(node) {
    this.visitNode(node);
  }, this);
};


/**
 * Generic visit function.  Calls VisitNode on all children node has.
 * @param {Object} node The node being dispatched to a visit call.
 * @protected
 */
glslunit.ASTVisitor.prototype.genericVisitor = function(node) {
  for (var field in node) {
    var item = node[field];
    if (goog.isArray(item)) {
      this.visitList(item);
    } else if (goog.isObject(item) && item.type) {
      this.visitNode(item);
    }
  }
};
